package com.xuecheng.content.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.content.model.dto.CourseBaseDTO;
import com.xuecheng.api.content.model.dto.TeachplanDTO;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.enums.common.CommonEnum;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.TeachPlanEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.StringUtil;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.convert.CourseBaseConvert;
import com.xuecheng.content.convert.TeachplanConvert;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.Teachplan;
import com.xuecheng.content.entity.TeachplanMedia;
import com.xuecheng.content.entity.TeachplanNode;
import com.xuecheng.content.mapper.TeachplanMapper;
import com.xuecheng.content.service.TeachplanMediaService;
import com.xuecheng.content.service.TeachplanService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 课程计划 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class TeachplanServiceImpl extends ServiceImpl<TeachplanMapper, Teachplan> implements TeachplanService {

    @Autowired
    private CourseBaseServiceImpl courseBaseService;

    @Autowired
    private TeachplanMediaService mediaService;

    /**
     * 课程计划查询
     * 业务分析：
     * 1.判断关键数据
     * courseId companyId
     * 2.判断业务数据
     * 课程基础信息
     * 判断是否存在
     * 判断是否是同一家教学机构
     * 判断是否删除
     * 3.根据Courseid查询课程计划数据
     * 4.通过java的递归生成课程计划树形结构
     * 5.将数据转为dto并返回
     *
     * @param courseId
     * @param companyId
     * @return
     */
    @Override
    public TeachplanDTO queryTreeNodesByCourseId(Long courseId, Long companyId) {
        if (ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(ContentErrorCode.E_120018);
        }

        if (ObjectUtils.isEmpty(courseId)) {
            ExceptionCast.cast(ContentErrorCode.E_120021);
        }

        CourseBaseDTO courseBase = courseBaseService.getCourseBaseById(courseId, companyId);

        if (!courseBase.getStatus().equals(CommonEnum.USING_FLAG.getCodeInt())) {
            ExceptionCast.cast(ContentErrorCode.E_120022);
        }

        TeachplanMapper baseMapper = this.getBaseMapper();
        List<TeachplanNode> rootNodes = baseMapper.selectTreeNodesByCourseId(courseId);

        TeachplanDTO teachplanDTO = null;

        if (CollectionUtils.isEmpty(rootNodes)) {
            //课程不存在
            //TODO
//            ExceptionCast.cast(ContentErrorCode.E_120013);

            teachplanDTO = new TeachplanDTO();
        } else {
            //1级
            TeachplanNode teachplanNode = rootNodes.remove(0);

            generateTreeNodes(teachplanNode, rootNodes);

            teachplanDTO = CourseBaseConvert.INSTANCE.node2dto(teachplanNode);
        }

        return teachplanDTO;
    }

    /**
     * 课程计划新增或者修改
     * <p>
     * 业务分析：
     * 主体方法
     * 1.判断业务操作
     * 判断是创建还是修改：teachplanId
     * 如果有:修改
     * 如果没有:创建
     * <p>
     * 2.执行创建和修改操作并返回dto数据
     * <p>
     * 3.返回结果数据dto
     *
     * @param teachplanDTO
     * @param companyId
     * @return
     */
    @Override
    public TeachplanDTO createOrModifyTeachPlan(TeachplanDTO teachplanDTO, Long companyId) {
        TeachplanDTO dto = null;

        if (ObjectUtils.isEmpty(teachplanDTO.getTeachPlanId())) {
            //创建
            dto = createTeachPlan(teachplanDTO, companyId);

        } else {
            //修改
            dto = modifyTeachPlan(teachplanDTO, companyId);
        }

        return dto;
    }

    /**
     * 删除课程计划
     * 1.判断关键
     * 		teachplanId  companyId
     * 2.判断业务数据
     * 	课程基础信息
     * 		判断是否存在、判断是否是同一家教学机构、判断是否删除、判断审核状态（未提交、审核未通过）
     * 	课程计划
     * 		判断删除的课程计划是几级数据
     * 			二级
     * 				判断是否存在子级数据，如果有不让删除
     * 			三级
     * 				如果是录播课程
     * 					判断课程计划是否有关联数据（查询TeachplanMedia表数据）
     * @param teachPlanId
     * @param companyId
     */
    @Override
    public void removeTeachPlan(Long teachPlanId, Long companyId) {

        //1.判断关键数据的合法性
        Teachplan teachplan = this.getById(teachPlanId);
        if (ObjectUtils.isEmpty(teachplan)) {

            ExceptionCast.cast(ContentErrorCode.E_120402);
        }

   
        //1.2 判断课程基本信息是否存在
        CourseBase courseBase = courseBaseService.getById(teachplan.getCourseId());

        if (ObjectUtils.isEmpty(courseBase)) {
            ExceptionCast.cast(ContentErrorCode.E_120013);
        }

        //1.3 判断公司 Id是否和课程中的公司Id一致
        if (!(ObjectUtils.nullSafeEquals(courseBase.getCompanyId(), companyId))) {
            ExceptionCast.cast(CommonErrorCode.E_100108);
        }

        // 2. 根据课程计划等级进行业务判断
        if (teachplan.getGrade().equals(TeachPlanEnum.SECEND_LEVEL)) {

            // 判断二级课程计划是否有子级课程计划信息
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getParentid, teachPlanId);
            int count = count(queryWrapper);


            if (count > 0) {
                ExceptionCast.cast(ContentErrorCode.E_120409);
            }

        } else {

            // 判断三级课程计划是否关联课程媒资信息
            LambdaQueryWrapper<TeachplanMedia> mediaLambdaQueryWrapper = new LambdaQueryWrapper<>();

            mediaLambdaQueryWrapper.eq(TeachplanMedia::getTeachplanId, teachPlanId);

            int mediaCount = mediaService.count(mediaLambdaQueryWrapper);

            if (mediaCount > 0) {
                ExceptionCast.cast(ContentErrorCode.E_120413);
            }

        }
        // 4.根据Id删除课程计划
        removeById(teachPlanId);
    }


    /**
     * 课程计划修改方法
     * 1.判断关键数据
     * 2.判断业务数据
     * 课程基础信息
     * 判断是否存在
     * 判断是否同一家机构
     * 判断是否删除
     * 判断课程基础信息审核状态：未提交和审核未通过
     * 课程计划
     * 判断是否存在
     * 3.修改课程计划数据
     * pname starttime endtime is_preview
     * 4.判断修改后的结果
     * 5.将数据库最新的数据转为dto并返回
     *
     * @param teachplanDTO
     * @param companyId
     * @return
     */
    private TeachplanDTO modifyTeachPlan(TeachplanDTO teachplanDTO, Long companyId) {
        //数据合法性验证
        CourseBaseDTO courseBaseDTO = verifyTeachplanMsg(teachplanDTO, companyId);

        //          判断是否存在
        LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Teachplan::getId, teachplanDTO.getTeachPlanId());
        int count = this.count(queryWrapper);
        if (count < 1) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }

        //修改课程计划数据
        LambdaUpdateWrapper<Teachplan> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(Teachplan::getPname, teachplanDTO.getPname());
        updateWrapper.set(Teachplan::getStartTime, teachplanDTO.getStartTime());
        updateWrapper.set(Teachplan::getEndTime, teachplanDTO.getEndTime());
        updateWrapper.set(Teachplan::getIsPreview, teachplanDTO.getIsPreview());
        updateWrapper.eq(Teachplan::getId, teachplanDTO.getTeachPlanId());

        boolean result = this.update(updateWrapper);

        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }

        //  5.将数据库最新的数据转为dto并返回
        Teachplan po = this.getById(teachplanDTO.getTeachPlanId());

        TeachplanDTO resultDTO = TeachplanConvert.INSTANCE.entity2dto(po);
        return resultDTO;
    }

    /**
     * 课程计划新增方法
     * 主体方法业务分析：
     * 1.判断关键数据
     * 2.判断业务数据
     * 课程基础信息
     * 判断是否存在
     * 判断是否同一家机构
     * 判断是否删除
     * 判断课程基础信息审核状态：未提交和审核未通过
     * 3.获得父级数据并进行关联
     * parentid
     * grade：子级grade是父级grade+1
     * orderby：获得父级课程计划的子级数据个数+1（bug）
     * 4.将dto转为po并保存
     * 5.判断保存后的结果
     * 6.将数据库最新数据转为dto并返回
     *
     * @param teachplanDTO
     * @param companyId
     * @return
     */
    private TeachplanDTO createTeachPlan(TeachplanDTO teachplanDTO, Long companyId) {
        //数据合法性验证
        CourseBaseDTO courseBaseDTO = verifyTeachplanMsg(teachplanDTO, companyId);
        //dto转po
        Teachplan teachplan = CourseBaseConvert.INSTANCE.dto2entity(teachplanDTO);
        Teachplan parentNode = generateParentNode(teachplanDTO, courseBaseDTO);
        //关联父类和子类
        teachplan.setParentid(parentNode.getId());
        teachplan.setGrade(parentNode.getGrade() + 1);

        TeachplanMapper baseMapper = this.getBaseMapper();
        Integer maxOrderby = baseMapper.getMaxOrderby(parentNode.getId());
        teachplan.setOrderby(maxOrderby + 1);

        // 4.将dto转为po并保存
        boolean result = this.save(teachplan);

        // 5.判断保存后的结果
        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }

        // 6.将数据库最新数据转为dto并返回
        Teachplan po = this.getById(teachplan.getId());

        TeachplanDTO resultDTO = TeachplanConvert.INSTANCE.entity2dto(po);

        return resultDTO;

    }

    private Teachplan generateParentNode(TeachplanDTO teachplanDTO, CourseBaseDTO courseBaseDTO) {

        if (ObjectUtils.isEmpty(teachplanDTO.getParentid())) {
            //2级

            //查数据库是否有该父类存在
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getCourseId, courseBaseDTO.getCourseBaseId());
            queryWrapper.eq(Teachplan::getParentid, TeachPlanEnum.FIRST_PARENTID_FLAG);
            Teachplan rootParent = this.getOne(queryWrapper);

            if (ObjectUtils.isEmpty(rootParent)) {
                //数据库中无父类
                rootParent = new Teachplan();
                rootParent.setPname(courseBaseDTO.getName());
                rootParent.setParentid(new Long(TeachPlanEnum.FIRST_PARENTID_FLAG));
                rootParent.setGrade(TeachPlanEnum.FIRST_LEVEL);
                rootParent.setDescription(courseBaseDTO.getDescription());
                rootParent.setOrderby(TeachPlanEnum.FIRST_LEVEL);
                rootParent.setCourseId(courseBaseDTO.getCourseBaseId());

                boolean save = this.save(rootParent);

                if (!save) {
                    ExceptionCast.cast(ContentErrorCode.E_120023);
                }
            }
            return rootParent;
        } else {
            //3级
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getId, teachplanDTO.getParentid());

            Teachplan teachplan = this.getOne(queryWrapper);

            if (ObjectUtils.isEmpty(teachplan)) {
                ExceptionCast.cast(ContentErrorCode.E_120408);
            }

            return teachplan;
        }
    }

    private CourseBaseDTO verifyTeachplanMsg(TeachplanDTO dto, Long companyId) {
        //1.判断关键数据
        if (ObjectUtils.isEmpty(companyId) ||
                StringUtil.isBlank(dto.getPname()) ||
                ObjectUtils.isEmpty(dto.getCourseId())
        ) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }


        // 2.判断业务数据
        //     课程基础信息
        //         判断是否存在
        //         判断是否同一家机构
        //         判断是否删除
        //         判断课程基础信息审核状态：未提交和审核未通过
        CourseBaseDTO courseBase = courseBaseService.getCourseBaseById(dto.getCourseId(), companyId);

        String auditStatus = courseBase.getAuditStatus();
        if (CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)
        ) {
            ExceptionCast.cast(ContentErrorCode.E_120015);
        }
        return courseBase;
    }

    /**
     * 生成树形结构
     *
     * @param parentNode
     * @param nodes
     */
    private void generateTreeNodes(TeachplanNode parentNode, List<TeachplanNode> nodes) {
        if (CollectionUtils.isEmpty(parentNode.getChildrenNodes())) {
            parentNode.setChildrenNodes(new ArrayList<TeachplanNode>(8));
        }

        for (TeachplanNode node : nodes) {
            if (ObjectUtils.nullSafeEquals(parentNode.getId(), node.getParentid())) {
                parentNode.getChildrenNodes().add(node);

                // 第三级课程计划
                if (!(TeachPlanEnum.THIRD_LEVEL.equals(node.getGrade()))) {
                    generateTreeNodes(node, nodes);
                }
            }
        }
    }
}
